import * as THREE from '../../build/three.module.js';
import Stats from '../../jsm/libs/stats.module.js';
import { OrbitControls } from '../../jsm/controls/OrbitControls.js';
import { Curves } from '../../jsm/curves/CurveExtras.js';

const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x000000, .004);

const camera = new THREE.PerspectiveCamera(40, innerWidth / innerHeight, 1, 1000);
camera.position.set(80, 40, 80);

scene.add(new THREE.AmbientLight(0xffffff, 1));

const light = new THREE.DirectionalLight(0xffffff, .4);
light.position.set(50, 40, 0);

light.castShadow = true;
light.shadow.camera.left = -40;
light.shadow.camera.right = 40;
light.shadow.camera.top = 40;
light.shadow.camera.bottom = -40;
light.shadow.camera.near = 10;
light.shadow.camera.far = 180;

light.shadow.bias = -.001;
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
scene.add(light);

// 阴影相机辅助线
light.shadowCameraHelper = new THREE.CameraHelper(light.shadow.camera);
scene.add(light.shadowCameraHelper);

const gmt = new THREE.InstancedBufferGeometry();
gmt.copy(new THREE.TorusBufferGeometry(2, .5, 8, 128));

const INSTANCES = 256;
let mesh;

const knot = new Curves.TorusKnot(10);
const positions = knot.getSpacedPoints(INSTANCES);

const offsets = new Float32Array(INSTANCES * 3), // xyz
      colors = new Float32Array(INSTANCES * 3), // rgb
      scales = new Float32Array(INSTANCES * 1) // scale

for (let i = 0; i < INSTANCES; i += 1) {
  const idx = i * 3;

  offsets[idx] = positions[i].x;
  offsets[idx + 1] = positions[i].y;
  offsets[idx + 2] = positions[i].z;

  colors[idx] = 1;
  colors[idx + 1] = 1;
  colors[idx + 2] = 1;

  scales[i] = 1 + .5 * Math.sin(32 * Math.PI * i / INSTANCES);
}

gmt.setAttribute( 'instanceOffset', new THREE.InstancedBufferAttribute( offsets, 3 ) );
gmt.setAttribute( 'instanceColor', new THREE.InstancedBufferAttribute( colors, 3 ) );
gmt.setAttribute( 'instanceScale', new THREE.InstancedBufferAttribute( scales, 1 ) );

const envMap = new THREE.TextureLoader().load('../../textures/metal.jpg', function (texture) {
  texture.mapping = THREE.SphericalReflectionMapping;
  texture.encoding = THREE.sRGBEncoding;
  if (mesh) mesh.material.needsUpdate = true;
})

const envMtl = new THREE.MeshLambertMaterial({
  color: 0xffb54a,
  envMap: envMap,
  combine: THREE.MultiplyOperation,
  reflectivity: .8,
  vertexColors: THREE.VertexColors,
  fog: true
})

envMtl.onBeforeCompile = function (shader) {
  shader.vertexShader = customLambertVertexShader;
}

const depMtl = new THREE.MeshDepthMaterial();
depMtl.onBeforeCompile = function (shader) {
  shader.vertexShader = customDepthVertexShader;
}
depMtl.depthPacking = THREE.RGBADepthPacking;

mesh = new THREE.Mesh(gmt, envMtl);
mesh.scale.set(1, 1, 2);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.customDepthMaterial = depMtl;
scene.add(mesh);

var ground = new THREE.Mesh(
  new THREE.PlaneBufferGeometry( 800, 800 ).rotateX( - Math.PI / 2 ),
  new THREE.MeshPhongMaterial( { color: 0x888888 } )
);
ground.position.set( 0, - 40, 0 );
ground.receiveShadow = true;

scene.add( ground );

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(scene.fog.color, 1);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableZoom = false;
controls.maxPolarAngle = Math.PI / 2;

const stats = new Stats();
document.body.appendChild(stats.dom);

const render = () => {
  mesh.rotation.y += .005;

  renderer.render(scene, camera);
}

const animate = () => {
  requestAnimationFrame(animate);

  stats.update();
  render();
}
animate();

const handleWindowResize = () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(innerWidth, innerHeight);
}
window.addEventListener('resize', handleWindowResize, false);